package jmine.tec.datadigester.web.pages.importacao;

import static jmine.tec.web.wicket.spring.SpringBeanReference.forName;
import images.ImageResources;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import jmine.tec.datadigester.dispatcher.DataDigesterDispatcher;
import jmine.tec.executionlog.dao.ExecutionLogDAO;
import jmine.tec.executionlog.domain.ExecutionLog;
import jmine.tec.executionlog.domain.enumx.ExecutionStatus;
import jmine.tec.web.pages.HelpTextMessages;
import jmine.tec.web.wicket.BeanReference;
import jmine.tec.web.wicket.component.button.ActionSubmitLink;
import jmine.tec.web.wicket.component.paging.PagingNavigatorContainer;
import jmine.tec.web.wicket.model.TecDatePropertyModel;
import jmine.tec.web.wicket.pages.Template;

import org.apache.wicket.RequestCycle;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
import org.apache.wicket.datetime.PatternDateConverter;
import org.apache.wicket.datetime.markup.html.form.DateTextField;
import org.apache.wicket.extensions.yui.calendar.DatePicker;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.PageableListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.util.ListModel;
import org.apache.wicket.request.target.resource.ResourceStreamRequestTarget;
import org.apache.wicket.util.time.Duration;

import bancosys.tec.datadigester.DataDigesterController;
import bancosys.tec.datadigester.input.ResetableInputStreamFactory;
import bancosys.tec.exception.MessageCreator;
import bancosys.tec.security.annotation.Secure;
import bancosys.tec.security.authorization.UrlPermission;
import bancosys.tec.utils.date.Date;
import bancosys.tec.utils.date.Timestamp;

/**
 * @author wagner
 */
@Secure(id = "URL_UPLOAD_ARQUIVOS", permissionType = UrlPermission.class)
public class Importacao extends Template {

    private static final int SECONDS_BETWEEN_REFRESH = 5;

    private static final String DEFAULT_DATE_FORMAT = "dd/MM/yyyy";

    private BeanReference<DataDigesterController> dataDigesterController = forName("dataDigesterController");

    private BeanReference<ExecutionLogDAO> executionLogDAO = forName("executionLogDAO");

    private BeanReference<DataDigesterDispatcher> dataDigesterDispatcher = forName("dataDigesterDispatcher");

    private static final int N_PAGES = 50;

    private FileUpload fileUpload;

    private String fileCode;

    private String fileCodeFiltro;

    private ExecutionStatus executionStatusFiltro;

    private Date dtInicioFiltro;

    private Date dtFimFiltro;

    private List<ExecutionLog> executionLogs;

    /**
     * @return the fileUpload
     */
    public FileUpload getFileUpload() {
        return this.fileUpload;
    }

    /**
     * @return the executionLogs
     */
    public List<ExecutionLog> getExecutionLogs() {
        return this.executionLogs;
    }

    /**
     * @param fileUpload the fileUpload to set
     */
    public void setFileUpload(FileUpload fileUpload) {
        this.fileUpload = fileUpload;
    }

    /**
     * @return the fileCode
     */
    public String getFileCode() {
        return this.fileCode;
    }

    /**
     * @param fileCode the fileCode to set
     */
    public void setFileCode(String fileCode) {
        this.fileCode = fileCode;
    }

    /**
     * C'tor
     */
    public Importacao() {
        this.initExecutionLogs();
        this.initForm();
    }

    /**
     * Inicializa os execution logs
     */
    private void initExecutionLogs() {
        // Utilizar esse metodo no lugar de findAll() para ter ordenação.
        this.executionLogs = this.executionLogDAO.get().findByTipoArquivoStatusTimestamp(null, null, null, null);
    }

    /**
     * Inicializa o form.
     */
    private void initForm() {
        this.add(new FeedbackPanel("feedback"));
        Form<Object> form = new Form<Object>("importForm");
        final Form<Object> formPesquisa = new Form<Object>("searchForm");

        final FileUploadField fileUploadField = new FileUploadField("fileUpload", new PropertyModel<FileUpload>(this, "fileUpload"));
        fileUploadField.setRequired(true);
        fileUploadField.setLabel(new Model<String>("arquivo"));
        form.add(fileUploadField);

        DropDownChoice<String> fileCodeSelect = this.createTipoArquivoSelect("fileCode");
        fileCodeSelect.setDefaultModel(new PropertyModel<String>(this, "fileCode"));
        form.add(fileCodeSelect);
        form.add(new ActionSubmitLink("btnImportar") {
            @Override
            public void onAction() throws Exception {
                Importacao.this.importarArquivo();
                Importacao.this.pesquisar();
            }
        });
        this.add(form);

        DateTextField dataInicioFiltro =
                new DateTextField("dtInicioFiltro", new TecDatePropertyModel(this, "dtInicioFiltro"), new PatternDateConverter(
                        DEFAULT_DATE_FORMAT, false));
        dataInicioFiltro.add(new DatePicker());
        formPesquisa.add(dataInicioFiltro);
        DateTextField dataFimFiltro =
                new DateTextField("dtFimFiltro", new TecDatePropertyModel(this, "dtFimFiltro"), new PatternDateConverter(
                        DEFAULT_DATE_FORMAT, false));
        dataFimFiltro.add(new DatePicker());
        formPesquisa.add(dataFimFiltro);

        DropDownChoice<String> fileCodeFiltroSelect = this.createTipoArquivoSelect("fileCodeFiltro");
        fileCodeFiltroSelect.setDefaultModel(new PropertyModel<String>(this, "fileCodeFiltro"));
        fileCodeFiltroSelect.setNullValid(true);
        formPesquisa.add(fileCodeFiltroSelect);

        DropDownChoice<ExecutionStatus> executionStatusFiltroSelect = this.createExecutionStatusSelect("executionStatusFiltro");
        executionStatusFiltroSelect.setDefaultModel(new PropertyModel<ExecutionStatus>(this, "executionStatusFiltro"));
        executionStatusFiltroSelect.setNullValid(true);
        formPesquisa.add(executionStatusFiltroSelect);
        PageableListView<ExecutionLog> listView =
                new PageableListView<ExecutionLog>("executionLogs", new PropertyModel<List<ExecutionLog>>(this, "executionLogs"), N_PAGES) {

                    @Override
                    protected void populateItem(ListItem<ExecutionLog> item) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
                        final ExecutionLog executionLog = item.getModelObject();
                        item.add(new Label("lstUsuario", executionLog.getUserName()));
                        item.add(new Label("lstTipoArquivo", executionLog.getFileCode()));
                        item.add(new Label("lstNomeArquivo", executionLog.getFileName()));
                        item.add(new Label("lstInicio", dateFormat.format(executionLog.getExecutionStartTime())));
                        item.add(new Label("lstDuracao", Importacao.this.milisecsAsTimeString(executionLog.getExecutionDuration())));

                        if (executionLog.getTotalItems() != null && executionLog.isRunning()) {
                            item.add(new Label("lstItens", executionLog.getProcessedItems() + "/" + executionLog.getTotalItems()));
                        } else {
                            item.add(new Label("lstItens", "-"));
                        }

                        item.add(new Image("lstStatus", new ResourceReference(ImageResources.class, this.statusImageFor(executionLog))));

                        Link<ExecutionLog> linkDownload = new Link<ExecutionLog>("download") {
                            @Override
                            public void onClick() {
                                ResourceStreamRequestTarget target =
                                        new ResourceStreamRequestTarget(new ExecutionLogResourceStream(executionLog.getId(),
                                                Importacao.this.dataDigesterController.get()));
                                target.setFileName(executionLog.getFileName());
                                RequestCycle.get().setRequestTarget(target);
                            }
                        };

                        linkDownload.add(new Image("icon", new ResourceReference(ImageResources.class, "file.png")));
                        item.add(linkDownload);
                        Link<ExecutionLog> linkDetalhe = new Link<ExecutionLog>("consultarDetalhe") {
                            @Override
                            public void onClick() {
                                this.setResponsePage(new ExecutionLogItens(executionLog));
                            }
                        };
                        linkDetalhe.add(new Image("icon", new ResourceReference(ImageResources.class, "magnifier.png")));
                        item.add(linkDetalhe);
                    }

                    private String statusImageFor(ExecutionLog executionLog) {
                        if (executionLog.isRunning()) {
                            return "loading.gif";
                        } else if (executionLog.isOk()) {
                            return "accept.png";
                        } else {
                            return "error.png";
                        }
                    }
                };

        WebMarkupContainer webMarkupContainer = new WebMarkupContainer("webMarkupContainer");
        webMarkupContainer.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(SECONDS_BETWEEN_REFRESH)) {
            @Override
            protected void onPostProcessTarget(AjaxRequestTarget target) {
                super.onPostProcessTarget(target);
                Importacao.this.pesquisar();
            }
        });
        webMarkupContainer.setOutputMarkupId(true);
        webMarkupContainer.add(listView);
        this.add(webMarkupContainer);

        this.add(new PagingNavigatorContainer("navigator", listView));

        formPesquisa.add(new ActionSubmitLink("btnPesquisar") {
            @Override
            public void onAction() {
                Importacao.this.pesquisar();
            }
        });

        this.add(formPesquisa);
    }

    /**
     * Converte quantidade de tempo em milisegundos para formato textual (HH:MM:SS). Copiado de TextualTimeConverter do tec.web.
     * <P>
     * XXX wagner.tsuchiya Onde colocar esse código?
     * </P>
     * 
     * @param milisecs Milisegundos
     * @return tempo em modo textual.
     */
    private String milisecsAsTimeString(Long milisecs) {
        final int mili = 1000;
        final int hora = 3600;
        final int minuto = 60;
        // Mili para segundos
        Long time = milisecs / mili;

        // Segundos para horas, minutos e segundos
        int horas = (int) (time / hora);
        int minutos = (int) (time % hora / minuto);
        int segundos = (int) (time % hora % minuto);

        Formatter f = new Formatter();
        f.format("%02d:%02d:%02d", horas, minutos, segundos);
        return f.toString();
    }

    /**
     * Cria o input select.
     * 
     * @param idComponente id do componente
     * @return {@link DropDownChoice}
     */
    private DropDownChoice<String> createTipoArquivoSelect(String idComponente) {
        ListModel<? extends String> listModel =
                new ListModel<String>(new ArrayList<String>(this.dataDigesterController.get().getAvailableProcessors()));
        DropDownChoice<String> select = new DropDownChoice<String>(idComponente, listModel);

        return select;
    }

    /**
     * Cria o input select.
     * 
     * @param idComponente id do componente
     * @return {@link DropDownChoice}
     */
    private DropDownChoice<ExecutionStatus> createExecutionStatusSelect(String idComponente) {
        List<ExecutionStatus> modelList = new ArrayList<ExecutionStatus>();
        for (ExecutionStatus executionStatus : ExecutionStatus.values()) {
            modelList.add(executionStatus);
        }
        ListModel<ExecutionStatus> listModel = new ListModel<ExecutionStatus>(modelList);
        DropDownChoice<ExecutionStatus> select = new DropDownChoice<ExecutionStatus>(idComponente, listModel);
        return select;
    }

    /**
     * Rodado no submit do form.
     * 
     * @throws IOException IOException
     */
    private void importarArquivo() throws IOException {
        final String code = Importacao.this.fileCode;
        final String fileName = Importacao.this.fileUpload.getClientFileName();
        final InputStream is = ResetableInputStreamFactory.createResetableInputStream(this.fileUpload.getInputStream());
        this.dataDigesterDispatcher.get().dispatch(code, fileName, is);
        this.info("Arquivo " + fileName + " importado com sucesso.");
    }

    /**
     * Pesquisa dado os parâmetros.
     */
    private void pesquisar() {
        Timestamp ini = this.dtInicioFiltro == null ? null : new Timestamp(this.dtInicioFiltro);
        Timestamp fin = this.dtFimFiltro == null ? null : new Timestamp(this.dtFimFiltro);
        this.executionLogs =
                this.executionLogDAO.get().findByTipoArquivoStatusTimestamp(this.fileCodeFiltro, this.executionStatusFiltro, ini, fin);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected MessageCreator getHelpTextCreator() {
        return HelpTextMessages.IMPORTACAO;
    }
}
